package com.theeviljames.base;

import java.util.Arrays;

import com.theeviljames.base.IMatrixOps;

public class MatrixOps implements IMatrixOps{

	private static IMatrixOps instance = null;
	public static IMatrixOps getMatrixOps(){
		if(instance == null) instance = new MatrixOps();
		return instance;
	}
	
	public double[] diag(double[][] a){
		int len = (a.length>a[0].length)?a[0].length:a.length;
		double[] result = new double[len];
		for(int i = 0; i < len; i++)result[i] = a[i][i];
		return result;
	}
	
	public double[][] append(double[][] a, double[][] b){
		if(a.length!=b.length) throw new RuntimeException("To append two matrices they must have an equal number of rows");
		double[][] result = new double[a.length][a[0].length+b[0].length];
		for(int i = 0; i < a.length; i++){
			for(int j = 0; j < a[0].length; j++){
				result[i][j] = a[i][j];
			}
			for(int j = a[0].length; j < result[0].length; j++){
				result[i][j] = b[i][j-a[0].length];
			}
		}
		return result;
	}
	
	public double[][] scalarDivideRow(double[][] a, int row, double denominator) {
		for(int i = 0; i < a[row].length; i++){
			a[row][i] = a[row][i]/denominator;
		}
		return a;
	}
	
	/**
	 * The values passed to this method are indexed from 1
	 */
	public double[][] extract(double[][] a, int x1, int y1, int x2, int y2) {
		double[][] result = new double[x2-(x1-1)][y2-(y1-1)];
		print(result);
		for(int i = x1-1; i < x2; i++){
			for(int j = y1-1; j < y2; j++){
				result[i-x1+1][j-y1+1] = a[i][j];
			}
		}
		return result;
	}
	
	public double[][] getIdentityMatrix(int n){
		double[][] result = new double[n][n];
		for(int i = 0; i < n; i++)result[i][i] = 1;
		return result;
	}
	
	public double[][] add(double[][] a, double[][] b) {
		double[][] c = a.clone();
		if(checkDims(a, b)){
			for(int i=0;i<a.length;i++){
				for(int j=0;j<a[i].length;j++){
					c[i][j] += b[i][j]; 
				}
			}
		}
		return c;
	}
	public double[][] cross(double[][] a, double[][] b) {
		if(!isColumnVector(b, 4)) throw new RuntimeException("Vector has incorrect dimensions");
		//print(b);
		double[][] cross = {{0,-a[0][2], a[0][1]},
							{a[0][2],0 , -a[0][0]},
							{-a[0][1],a[0][0],0}};
		//double[][] b2 = new double[b.length-1][b[0].length];
//		print(b2);
//		print(cross);
//		print(b2);
		return times(cross, b);
	}
	public double[][] dot(double[][] a, double[][] b) {
		if(!isRowVector(a, 4)||!isRowVector(b, 4))throw new RuntimeException("Matrices must be row vectors with length 4");
		return times(a,transpose(b));
	}
	public double[][] inverse(double[][] a) {
		// TODO Auto-generated method stub
		return null;
	}
	
	public double[][] transpose(double[][] a) {
		if(a.length == 0) return a;
		int m = a.length;
		int n = a[0].length;
		double[][] b = new double[n][m];
		for(int i = 0; i < m; i++){
			for(int j = 0; j < n; j++){
				b[j][i] = a[i][j];
			}
		}
		return b;
	}
	
	public double[][] minus(double[][] a, double[][] b) {
		double[][] c = a.clone();
		if(checkDims(a, b)){
			for(int i=0;i<a.length;i++){
				for(int j=0;j<a[i].length;j++){
					c[i][j] -= b[i][j]; 
				}
			}
		}
		return c;
	}
	public double[][] scalarTimes(double[][] a, double b) {
		double[][] c = a.clone();
		for(int i = 0 ; i < a.length; i++){
			for(int j = 0; j < a[0].length; j++){
				c[i][j] = a[i][j]*b;
			}
		}
		return c;
	}
	public double[][] times(double[][] a, double[][] b) {
		
		int m = a[0].length;
		int n = a.length;
		
		int p = b[0].length;
		double[][] c = new double[n][p];
		
		for(int i=0;i<n;i++){
			for(int j=0;j<p;j++){
				double val = 0;
				for(int k=0;k<m;k++){
					val += a[i][k] * b[k][j];
				}
				c[i][j] = val;
			}
		}
		return c;
	}
	public boolean isColumnVector(double[][] a, double s) {
		//if(a.length != 1) return false;
		int i = 0;
		for(;i<a.length;i++){
			if(i>s-1) return false;
			if(a[i].length != 1) return false;
		}
		if(i<s)return false;
		
		// TODO Auto-generated method stub
		return true;
	}
	public boolean isRowVector(double[][] a, double s) {
		if(a.length != 1) return false;
		for(int i=0;i<a[0].length;i++){
			if(i>s-1) return false;
		}
		// TODO Auto-generated method stub
		return true;
	}
	public boolean checkDims(double[][] a, double[][] b) {
		// Matrix dims must agree
		if(a.length != 0 && b.length != 0)
			if(a.length == b.length){
				if(a[0].length == b[0].length){
					return true;
				}
			}
 		return false;
	}
	public void print(double[][] a){
		for(double[] d:a){
			System.out.println(Arrays.toString(d));
		}
		System.out.println();
	}
	
	public double length(double[][] a) {
		double sum = 0.0;
		for(int i = 0 ; i < a.length; i++){
			for(int j = 0; j < a[0].length; j++){
				sum+=Math.pow(a[i][j],2);
			}
		}
		return Math.sqrt(sum);
	}
	
	public double[][] normalize(double[][] a) {
		double len = 1/length(a);
		return scalarTimes(a, len);
	}
	public static void main(String[] args) {
/*		IMatrixOps m = getMatrixOps();
		double[][] a  = new double[][]{{1},{2}};
		System.out.println("Matrix A");
		m.print(a);
		double[][] b  = new double[][]{{1,2},{3,4}};
		System.out.println("Matrix B");
		m.print(b);
		double[][] c = m.times(b,a);
		System.out.println("Matrix C");
		m.print(c);
*/	
		double[][] a = new double[][]{{1,2,3},{3,4,5},{5,6,7}};
		double[][] c = getMatrixOps().extract(a,1,1,2,2);
		getMatrixOps().print(c);
	}
	
	
}
